# 产物分析

## 功能介绍

**Rsdoctor** 提供了 `Bundle Size` 模块，该模块主要用于分析 **Webpack** 或 **Rspack** 构建产物的信息，包括当前编译产物的**资源大小**、**重复包**、**模块引用关系**等：

- **产物概览**：展示产物总数、各类型文件数目、大小以及占比，以及重复包和重复包的引用链；
- **产物分析（`Bundle Analysis`）模块**：分析构建产物资源（**Assets**）以及所包含的 **Modules** 的大小和代码信息。在该模块中，可以查看 Assets 中 **Module 打包后的实际代码大小**，以及模块的原始代码或**打包后的代码段**以及**模块引用关系**。

<img
  src="https://assets.rspack.rs/others/assets/rsdoctor/bundle-size-overall.png"
  width={'700px'}
  style={{ margin: 'auto' }}
/>

点击**导航栏 「Bundle Size」-> 「Bundle Size」选项**，即可查看 Bundle 分析报告。请注意，要展示此页面，需要开启构建产物分析能力 [features](/config/options/options)。

### 名词解释

- **`Assets`**：资源是对图像、字体、媒体和其他文件类型的统称，是最终存在于输出文件夹内的文件，同时，每个 Chunk 都有对应的 [Assets 资源](https://webpack.js.org/concepts/under-the-hood/#chunks)。
- **`Module`**：一个或多个 Module 组合成了 Chunk。有关 Module 类型的详细信息，请参阅 [Rspack Modules](https://www.rspack.rs/api/modules.html) 和 [Webpack Modules](https://webpack.js.org/concepts/modules/#what-is-a-webpack-module)。
- **`Source Size`**：文件的原始大小，未经过任何转换和压缩。
- **`Bundle Size`**：文件最终输出的大小。如果开启了压缩，这个值代表压缩后的大小。
- **`Package Count`**：第三方包的数量。
- **`Initial Chunk`**: **initial(初始化)** 是入口起点的主 Chunk，该 chunk 包含入口起点指定的所有模块及其依赖项，与「**按需加载**」的 **Chunk** 资源不同。
  - 有关 Initial Chunk 的详细信息，请参阅 [Initial Chunk 介绍](https://webpack.js.org/concepts/under-the-hood/#chunks)。
- **`Duplicate Packages`**: 打包到项目中的重复的第三方包。不包括未打包进产物的第三方包，请参阅[重复第三方包](/guide/usage/bundle-alerts)。
- **`Concatenated Module`**: 串联模块是将多个模块在打包时提升或串联到一个闭包中。在过去，Webpack 在打包时会将每个模块都打包到单独的闭包中，这种封装函数会导致 JavaScript 在浏览器中执行时较慢。可以通过打开 [`optimization.concatenateModules`](https://rspack.rs/zh/misc/glossary#scope-hoisting-%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%90%88%E5%B9%B6) 参数来进行优化。

## 产物概览

### 产物信息卡

产物概览显示了 `Total Files` 等文件数目和大小信息。点击卡片图表可以展开资源详情，如下图所示：

<img
  src="https://assets.rspack.rs/others/assets/rsdoctor/bundle-size-overall-1.png"
  width={'700px'}
  style={{ margin: 'auto' }}
/>

- 点击详情小图标，右侧会展示对应资源树，标明资源大小：

<img
  src="https://assets.rspack.rs/others/assets/rsdoctor/bundle-size-tree.png"
  width={'300px'}
  height={'400px'}
  style={{ margin: 'auto' }}
/>

- 点击标签切换资源信息查看，例如：**「Total JS | Initial JS」**。同时，卡片上展示了资源的体积占比、体积大小以及资源数目。同样，点击右下图标可以展开资源列表。

### 重复第三方包

- **Duplicate Packages** 卡片上展示了项目中重复的第三方包数量。点击图片可以查看重复第三方包的具体详情。请注意，这里的第三方包是被打包的第三方包。

更多信息，请参阅[重复第三方包](/guide/usage/bundle-alerts)

## 产物分析

::: tip
如果项目基于 Rspack 且版本低于 0.5.1，无法查看代码信息。
:::

### 资源与模块关系展示

**Bundle Analysis** 模块用于分析构建产物资源（**Assets**）以及所包含的 **Modules** 的大小和代码信息。示例图如下：

- 左侧是 **Assets** 资源列表，按照资源大小降序排列，可以点击 **「expand all」** 按钮展开全部节点。
- 右侧是 **Assets** 对应的 **Modules** 列表，同样按照打包后模块大小降序排列。

<img src="https://assets.rspack.rs/others/assets/rsdoctor/bundle-size-analysis-tree.png" />

### 搜索筛选框

顶部工具栏从左到右依次是：搜索 `Assets` 工具、筛选 `Assets` 大小工具、筛选 `Module` 大小工具。

- **搜索 Entry 输入框**：在输入框中输入 **Entry** 的关键词，可以搜索对应的 **Entry**，只展示该 **Entry** 下的 **Assets** 等。
- **搜索 Assets 输入框**：在输入框中输入 `Assets` 的关键词，可以搜索对应的 `Assets`。
- **Assets Size 筛选工具**：填写数字，单位为：KB、MB，可以过滤掉小于所填 `Size` 的 `Assets` 资源。
- **Module Size 筛选工具**：填写数字，单位为：KB、MB，可以过滤掉小于所填 `Size` 的 `Module` 资源。

<img src="https://assets.rspack.rs/others/assets/rsdoctor/bundle-size-analysis-selects.png" />

#### 模块搜索功能

支持了模块搜索功能，用户可以通过点击「**Search Module**」按钮，打开模块搜索弹窗。输入模块名称来快速定位和查看模块所在的 Assets，从而更方便地分析模块的引用关系和大小。搜索 Module 位于哪些 Assets 中。

如下图，可以看到匹配搜素 Module 关键字的结果：

<img src="https://assets.rspack.rs/others/assets/rsdoctor/search-modules.png" />

### 模块标签说明

**Assets** 标签如左图所示，从左到右依次代表：**资源体积**、**[Initial Chunk](https://webpack.js.org/concepts/under-the-hood/#chunks)** 和 **代码查看**。

<div style={{ display: 'flex' }}>
  <img
    src="https://assets.rspack.rs/others/assets/rsdoctor/bundle-size-assets-tags.png"
    height="200px"
    width="260px"
    style={{ margin: 'auto' }}
  />
  <img
    src="https://assets.rspack.rs/others/assets/rsdoctor/bundle-size-modules-tags.png"
    height="300px"
    width="340px"
    style={{ margin: 'auto' }}
  />
</div>

**Modules** 标签如右图所示，从左到右依次代表：

- **Bundled Size**
  - 模块打包进产物的最终大小。一些标有 `concatenated` 的模块是串联模块，对此值有一定影响，请参阅下方的 `concatenated module` 解释。
- **[Concatenated Module](https://rspack.rs/zh/misc/glossary#scope-hoisting-%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%90%88%E5%B9%B6)** 串联模块，串联模块是将多个模块在打包时提升或串联到一个闭包中。有两种类型：
  - 一种是串联主模块，标明串联了多少个 `Modules`；
  - 另一种是被串联的子模块，标明被聚合到了哪个 `Module` 内。这种子模块无法继续拆解打包后的代码，因此无法得知具体的 `Bundled Size`，只能得知整个串联模块的大小，该大小标注在主模块尾部。
- **Module Explorer** 标签：点击可打开 `Module` 之间的依赖关系分析页面。
- **代码查看** 标签，点击可展开代码段，包括 `Source`（源码）、`Transformed`（编译后代码）和 `Bundled`（打包后代码）。

### 支持 concatenated module 分析

首先，**[Concatenated Module](https://rspack.rs/zh/misc/glossary#scope-hoisting-%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%90%88%E5%B9%B6)** 是多个 Modules 被合并在了一个闭包中，无法通过 AST 语法分析拆解，但 concatenated module 中可能包含不同 Package 的代码，分析 concatenated module 内部子 Modules 也成为了重点，尤其是采用 ["all-in-one"](https://rsbuild.rs/guide/optimization/code-splitting#all-in-one) 方式打包的项目。

Rsdoctor 支持了对 **[Concatenated Module](https://rspack.rs/zh/misc/glossary#scope-hoisting-%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%90%88%E5%B9%B6)** 的拆解并精确计算 concatenated module 中子 modules 的真实的打包后的体积，帮助开发者准确识别 Tree Shaking 后的实际构建体积，分析合并模块对最终 bundle 大小的影响以及优化分包策略。

- **Rspack 项目**，Rspack（>=1.4.11）内部的 Rsdoctor native 插件也增强了 sourcemap 相关能力，可以在不开启 source map 的情况下，无痛分析 concatenate module。

> 可以看到下图，是 'all-in-one' 的打包分析，上图为之前无法分析的情况，下图为可分析情况

- 不支持分析

<img
  src="https://assets.rspack.rs/others/assets/rsdoctor/all-in-one-before.png"
  width="600px"
  style={{ margin: 'auto' }}
/>

- 支持分析

<img
  src="https://assets.rspack.rs/others/assets/rsdoctor/all-in-one-after.png"
  width="600px"
  style={{ margin: 'auto' }}
/>

- **webpack 项目**，则需要开启 sourcemap，才可以准确拆解并分析 concatenated module，如下配置。

```js
export default {
  // ...
  devtool: 'cheap-source-map', // 或其他 devtool 配置
};
```

- Rsdoctor 对 Concatenated Module 分析支持如下 Sourcemap 配置：
  - source-map
  - hidden-source-map
  - inline-source-map
  - cheap-source-map
  - cheap-module-source-map
  - nosources-source-map

### Module 详情

点击模块标签，可以查看模块详情，如下图所示：

<img src="https://assets.rspack.rs/others/assets/rsdoctor/bailout-reason.gif" />

- Reasons : 顾名思义是 [原因] 的意思，即某个 Module 存在的原因。Reasons 就是该 Module 被哪些 Module 们引入，而整个 Reasons Tree 就是这个 Module 的整个上游引用链，包括了直接父级和间接父级们。即 Rspack 的 stats.reasons。
- Dependencies : 是该 Module 依赖了哪些 Module。
- Bailout Reason : Tree shaking 时，该 Module Tree shaking 失败的原因。

> 更多可查看详情：[Module 详情](/guide/usage/module-analysis)

## 产物总览瓦片图

点击 **「Bundle Size」** 页面的 **「Treemap Graph」** 标签，可以查看瓦片图。通过瓦片图可以清晰的看到各个资源和模块之间的占比和关系，如下图所示：

<img
  src="https://assets.rspack.rs/others/assets/rsdoctor/treemap.png"
  width="500px"
  style={{ margin: 'auto' }}
/>

同时可以点击卡片标题侧的 🔍 按钮，搜索 Module 资源，点击 Module 资源，可以放大到该 Module 区域，如下图所示：

<img
  src="https://assets.rspack.rs/others/assets/rsdoctor/treemap.gif"
  width="500px"
  style={{ margin: 'auto' }}
/>

## 支持 BannerPlugin

:::danger
`supports.banner` 选项仅用于调试，请勿将其用于生产。
:::

由于 Rspack 和 webpack 都支持 [BannerPlugin](https://www.rspack.rs/plugins/webpack/banner-plugin#bannerplugin)，BannerPlugin 是可在生成的 chunk 顶部或尾部添加一段指定的内容的内置插件。

添加的代码段将会影响到对 Bundle 的解析能力。

Rsdoctor 兼容了对 BannerPlugin 这种添加代码的逻辑，但因为 Rsdoctor 需要添加标记代码，所以没有默认开启，以下两种情况会开启 Rsdoctor BannerPlugin 能力：

1. 项目在 `rspack.config.(js|ts)` 或 `webpack.config.(js|ts)` 中使用了 BannerPlugin；

2. 通过 Rsdoctor options 配置 `supports.banner` 来开启：

```ts
new RsdoctorRspackPlugin({
  supports: {
    banner: true,
  },
});
```

- 注：如果开启了 `drop_console` 将会影响 Rsdoctor 对 BannerPlugin 的分析，所以可以在 `RSDOCTOR = true` 时，关闭 `drop_console`。
